package com.itcam.web.controller;

import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import com.itcam.maker.generator.main.GenerateTemplate;
import com.itcam.maker.generator.main.ZipGenerator;
import com.itcam.maker.meta.MetaValidator;
import com.itcam.web.annotation.AuthCheck;
import com.itcam.web.common.BaseResponse;
import com.itcam.web.common.DeleteRequest;
import com.itcam.web.common.ErrorCode;
import com.itcam.web.common.ResultUtils;
import com.itcam.web.constant.UserConstant;
import com.itcam.web.exception.BusinessException;
import com.itcam.web.exception.ThrowUtils;
import com.itcam.web.manager.CacheManager;
import com.itcam.web.manager.CosManager;
import com.itcam.maker.meta.Meta;
import com.itcam.web.model.dto.generator.*;
import com.itcam.web.model.entity.Generator;
import com.itcam.web.model.entity.User;
import com.itcam.web.model.vo.GeneratorVO;
import com.itcam.web.service.GeneratorService;
import com.itcam.web.service.UserService;
import com.qcloud.cos.model.COSObject;
import com.qcloud.cos.model.COSObjectInputStream;
import com.qcloud.cos.utils.IOUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

/**
 * 生成器接口
 *
 */
@RestController
@RequestMapping("/generator")
@Slf4j
public class GeneratorController {

    @Resource
    private GeneratorService generatorService;

    @Resource
    private UserService userService;

    @Resource
    private CosManager cosManager;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private CacheManager cacheManager;

    // region 增删改查

    /**
     * 创建
     *
     * @param generatorAddRequest
     * @param request
     * @return
     */
    @PostMapping("/add")
    public BaseResponse<Long> addGenerator(@RequestBody GeneratorAddRequest generatorAddRequest, HttpServletRequest request) {
        if (generatorAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorAddRequest, generator);
        List<String> tags = generatorAddRequest.getTags();
        generator.setTags(JSONUtil.toJsonStr(tags));
        Meta.FileConfig fileConfig = generatorAddRequest.getFileConfig();
        generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        Meta.ModelConfig modelConfig = generatorAddRequest.getModelConfig();
        generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));

        // 参数校验
        generatorService.validGenerator(generator, true);
        User loginUser = userService.getLoginUser(request);
        generator.setUserId(loginUser.getId());
        generator.setStatus(0);
        boolean result = generatorService.save(generator);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        long newGeneratorId = generator.getId();
        return ResultUtils.success(newGeneratorId);
    }

    /**
     * 删除
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteGenerator(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = userService.getLoginUser(request);
        long id = deleteRequest.getId();
        // 判断是否存在
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可删除
        if (!oldGenerator.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        boolean b = generatorService.removeById(id);
        return ResultUtils.success(b);
    }

    /**
     * 更新（仅管理员）
     *
     * @param generatorUpdateRequest
     * @return
     */
    @PostMapping("/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateGenerator(@RequestBody GeneratorUpdateRequest generatorUpdateRequest) {
        if (generatorUpdateRequest == null || generatorUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorUpdateRequest, generator);
        List<String> tags = generatorUpdateRequest.getTags();
        generator.setTags(JSONUtil.toJsonStr(tags));
        Meta.FileConfig fileConfig = generatorUpdateRequest.getFileConfig();
        generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        Meta.ModelConfig modelConfig = generatorUpdateRequest.getModelConfig();
        generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));

        // 参数校验
        generatorService.validGenerator(generator, false);
        long id = generatorUpdateRequest.getId();
        // 判断是否存在
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        boolean result = generatorService.updateById(generator);
        return ResultUtils.success(result);
    }

    /**
     * 根据 id 获取
     *
     * @param id
     * @return
     */
    @GetMapping("/get/vo")
    public BaseResponse<GeneratorVO> getGeneratorVOById(long id, HttpServletRequest request) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = generatorService.getById(id);
        if (generator == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        return ResultUtils.success(generatorService.getGeneratorVO(generator, request));
    }

    /**
     * 分页获取列表（仅管理员）
     *
     * @param generatorQueryRequest
     * @return
     */
    @PostMapping("/list/page")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<Generator>> listGeneratorByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest) {
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));
        return ResultUtils.success(generatorPage);
    }

    /**
     * 分页获取列表（封装类）
     *
     * @param generatorQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/page/vo")
    public BaseResponse<Page<GeneratorVO>> listGeneratorVOByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest,
                                                                 HttpServletRequest request) {
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);

//        StopWatch stopWatch = new StopWatch();
//        stopWatch.start();

        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));

//        stopWatch.stop();
//        System.out.println("查询生成器耗时：" + stopWatch.getTotalTimeMillis());

//        stopWatch = new StopWatch();
//        stopWatch.start();

        Page<GeneratorVO> generatorVOPage = generatorService.getGeneratorVOPage(generatorPage, request);

//        stopWatch.stop();
//        System.out.println("查询关联数据耗时" + stopWatch.getTotalTimeMillis());

        return ResultUtils.success(generatorVOPage);
    }

    /**
     * 快速分页获取列表（封装类）
     *
     * 该方法用于快速分页获取生成器视图对象列表，通过限制每页大小防止爬虫，
     * 并移除生成器视图对象中的文件配置和模型配置信息以提高响应速度。
     *
     * @param generatorQueryRequest 生成器查询请求对象，包含分页和查询条件信息
     * @param request HTTP请求对象，用于获取请求相关信息
     * @return 返回封装了生成器视图对象分页信息的响应对象
     */
    @PostMapping("/list/page/vo/fast")
    public BaseResponse<Page<GeneratorVO>> listGeneratorVOByPageFast(@RequestBody GeneratorQueryRequest generatorQueryRequest,
                                                                     HttpServletRequest request) {
        // 从查询请求中获取当前页码
        long current = generatorQueryRequest.getCurrent();
        // 从查询请求中获取每页显示的记录数
        long size = generatorQueryRequest.getPageSize();

        // 优先从缓存读取
        String cacheKey = getPageCacheKey(generatorQueryRequest);
//        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
//        String cacheValue = valueOperations.get(cacheKey);

        // 多级缓存
        Object cacheValue = cacheManager.get(cacheKey);
//        if (StrUtil.isNotBlank(cacheValue)) {
        if (null != cacheValue) {
//            Page<GeneratorVO> generatorVOPage = JSONUtil.toBean(cacheValue,
//                    new TypeReference<Page<GeneratorVO>>() {
//                    },
//                    false);
            return ResultUtils.success((Page<GeneratorVO>) cacheValue);
        }

        // 限制爬虫，若每页记录数超过20，抛出参数错误异常
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);

        // 优化sql语句，只查出需要展示的字段
        QueryWrapper<Generator> queryWrapper = generatorService.getQueryWrapper(generatorQueryRequest);
        queryWrapper.select("id", "name", "description", "tags", "picture", "status", "userId", "createTime", "updateTime");

        // 调用服务层的page方法进行分页查询，获取生成器分页对象
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size), queryWrapper);
        // 将生成器分页对象转换为生成器视图对象分页对象
        Page<GeneratorVO> generatorVOPage = generatorService.getGeneratorVOPage(generatorPage, request);
        // 遍历生成器视图对象列表，将文件配置和模型配置信息置为null，以减少数据量
//        generatorVOPage.getRecords().forEach(generatorVO -> {
//            generatorVO.setFileConfig(null);
//            generatorVO.setModelConfig(null);
//        });

        // 写入缓存
//        valueOperations.set(cacheKey, JSONUtil.toJsonStr(generatorVOPage), 100, TimeUnit.MINUTES);
        cacheManager.put(cacheKey, JSONUtil.toJsonStr(generatorVOPage));
        // 返回包含生成器视图对象分页信息的成功响应
        return ResultUtils.success(generatorVOPage);
    }

    /**
     * 获取分页缓存 key
     *
     * 该方法用于根据传入的 GeneratorQueryRequest 对象生成一个唯一的分页缓存键。
     * 具体步骤为：先将 GeneratorQueryRequest 对象转换为 JSON 字符串，
     * 然后对该 JSON 字符串进行 Base64 编码，最后将编码后的字符串与固定前缀 "generator:page" 拼接，
     * 得到最终的缓存键。这样可以确保不同的查询请求对应不同的缓存键，避免缓存冲突。
     *
     * @param generatorQueryRequest 生成器查询请求对象，包含分页和查询条件信息
     * @return 返回根据查询请求生成的唯一分页缓存键
     */
    public static String getPageCacheKey(GeneratorQueryRequest generatorQueryRequest) {
        // 将 GeneratorQueryRequest 对象转换为 JSON 字符串
        String jsonStr = JSONUtil.toJsonStr(generatorQueryRequest);
        // 对 JSON 字符串进行 Base64 编码
        String base64 = Base64Encoder.encode(jsonStr);
        // 拼接固定前缀和 Base64 编码后的字符串，生成最终的缓存键
        String key = "generator:page" + base64;
        // 返回生成的缓存键
        return key;
    }


    /**
     * 分页获取当前用户创建的资源列表
     *
     * @param generatorQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/my/list/page/vo")
    public BaseResponse<Page<GeneratorVO>> listMyGeneratorVOByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest,
                                                                   HttpServletRequest request) {
        if (generatorQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User loginUser = userService.getLoginUser(request);
        generatorQueryRequest.setUserId(loginUser.getId());
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));
        return ResultUtils.success(generatorService.getGeneratorVOPage(generatorPage, request));
    }

    // endregion

    /**
     * 编辑（用户）
     *
     * @param generatorEditRequest
     * @param request
     * @return
     */
    @PostMapping("/edit")
    public BaseResponse<Boolean> editGenerator(@RequestBody GeneratorEditRequest generatorEditRequest, HttpServletRequest request) {
        if (generatorEditRequest == null || generatorEditRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorEditRequest, generator);
        List<String> tags = generatorEditRequest.getTags();
        generator.setTags(JSONUtil.toJsonStr(tags));
        Meta.FileConfig fileConfig = generatorEditRequest.getFileConfig();
        generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        Meta.ModelConfig modelConfig = generatorEditRequest.getModelConfig();
        generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));

        // 参数校验
        generatorService.validGenerator(generator, false);
        User loginUser = userService.getLoginUser(request);
        long id = generatorEditRequest.getId();
        // 判断是否存在
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可编辑
        if (!oldGenerator.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        boolean result = generatorService.updateById(generator);
        return ResultUtils.success(result);
    }

    /**
     *
     *
     * @param id
     * @return
     */
    @GetMapping("/download")
    public void downloadGeneratorById(long id, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User loginUser = userService.getLoginUser(request);
        Generator generator = generatorService.getById(id);
        if (generator == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }

        String dishPath = generator.getDistPath();
        if (StrUtil.isBlank(dishPath)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "产物包不存在");
        }

        // 追踪事件
        log.info("用户 {} 下载了 {}", loginUser, dishPath);

        // 设置响应头
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + dishPath);

        // 优先从缓存读取
        String zipFilePath = getCacheFilePath(id, dishPath);
        if (FileUtil.exist(zipFilePath)) {
            // 写入响应
            Files.copy(Paths.get(zipFilePath), response.getOutputStream());
            return;
        }

        COSObjectInputStream cosObjectInput = null;
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();

            COSObject cosObject = cosManager.getObject(dishPath);
            cosObjectInput = cosObject.getObjectContent();
            // 处理下载到的流
            byte[] bytes = IOUtils.toByteArray(cosObjectInput);

            stopWatch.stop();
            System.out.println(stopWatch.getTotalTimeMillis());

            // 设置响应头
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + dishPath);
            // 写入响应
            response.getOutputStream().write(bytes);
            response.getOutputStream().flush();
        } catch (Exception e) {
            log.error("file download error, filepath = " + dishPath, e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "下载失败");
        } finally {
            if (null != cosObjectInput) {
                cosObjectInput.close();
            }
        }
    }

    /**
     * 使用代码生成器
     *
     * @param generatorUseRequest 请求参数，包含生成器ID和数据模型
     * @param request HTTP请求对象
     * @param response HTTP响应对象
     */
    @PostMapping("/use")
    public void useGenerator(
            @RequestBody GeneratorUseRequest generatorUseRequest,
            HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        // 1.获取用户输入的请求参数
        Long id = generatorUseRequest.getId();
        Map<String, Object> dataModel = generatorUseRequest.getDataModel();

        // 2.需要用户登录
        User loginUser = userService.getLoginUser(request);
        log.info("userId = {} 使用了生成器 id = {}", loginUser.getId(), id);

        Generator generator = generatorService.getById(id);
        if (generator == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }

        // 3.生成器的存储路径
        String distPath = generator.getDistPath();
        if (StrUtil.isBlank(distPath)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "产物包不存在");
        }

        // 4.从对象存储下载生成器的压缩包
        // 4.1 定义独立的工作空间
        // 获取当前项目的路径
        String projectPath = System.getProperty("user.dir");
        // 构造临时目录路径，用于存放下载的压缩包和生成的文件
        String tempDirPath = String.format("%s/.temp/use/%s", projectPath, id);
        // 构造压缩包文件路径
        String zipFilePath = tempDirPath + "/dish.zip";
        // 4.2 不存在，新建文件
        // 检查压缩包文件是否存在，如果不存在则创建
        if (!FileUtil.exist(zipFilePath)) {
            FileUtil.touch(zipFilePath);
        }

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 4.3 下载文件
        try {
            // 调用cosManager的download方法下载生成器压缩包到指定路径
            cosManager.download(distPath, zipFilePath);
        } catch (InterruptedException e) {
            // 如果下载过程中发生中断异常，抛出业务异常
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "生成器下载失败");
        }
        stopWatch.stop();
        System.out.println("下载耗时：" + stopWatch.getTotalTimeMillis());

        stopWatch = new StopWatch();
        stopWatch.start();
        // 5.解压文件，得到用户上传的生成器文件
        // 使用ZipUtil工具类解压下载的压缩包
        File unzipped = ZipUtil.unzip(zipFilePath);
        stopWatch.stop();
        System.out.println("解压耗时：" + stopWatch.getTotalTimeMillis());

        stopWatch = new StopWatch();
        stopWatch.start();
        // 6.将用户输入的参数写入到JSON文件中
        // 构造数据模型文件路径
        String dataModelFilePath = tempDirPath + "/dataModel.json";
        // 将数据模型转换为JSON字符串
        String jsonStr = JSONUtil.toJsonStr(dataModel);
        // 将JSON字符串写入到文件中
        FileUtil.writeUtf8String(jsonStr, dataModelFilePath);
        stopWatch.stop();
        System.out.println("写数据文件耗时：" + stopWatch.getTotalTimeMillis());

        // 7.执行脚本
        // 7.1 找到脚本文件所在路径
        // 使用FileUtil的loopFiles方法遍历解压后的目录，查找名为generator.bat的文件
        File scriptFile = FileUtil.loopFiles(unzipped, 2, null)
                .stream()
                .filter(file -> file.isFile() && "generator.bat".equals(file.getName()))
                .findFirst()
                .orElseThrow(RuntimeException::new);

        // 7.2 添加可执行权限
        try {
            // 设置脚本文件的权限为可执行
            Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rwxrwxrwx");
            Files.setPosixFilePermissions(scriptFile.toPath(), permissions);
        } catch (IOException e) {
            // 处理异常，例如记录日志或抛出更具体的异常
        }

        // 8.构造命令
        // 获取脚本文件所在的目录
        File scriptDir = scriptFile.getParentFile();
        // 获取脚本文件的绝对路径，并将路径中的反斜杠替换为正斜杠
        String scriptAbsolutePath = scriptFile.getAbsolutePath().replace("\\", "/");
        // 构造命令数组，包括脚本文件路径和参数
        String[] commands = {scriptAbsolutePath, "json-generate", "--file=" + dataModelFilePath};

        // 创建ProcessBuilder对象，用于执行命令
        ProcessBuilder processBuilder = new ProcessBuilder(commands);
        // 设置命令执行的工作目录
        processBuilder.directory(scriptDir);

        try {
            stopWatch = new StopWatch();
            stopWatch.start();
            // 启动命令进程
            Process process = processBuilder.start();
            // 获取命令的输出流
            InputStream inputStream = process.getInputStream();
            // 使用BufferedReader读取输出流
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            // 逐行读取输出并打印
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            // 等待命令执行完成，并获取退出码
            int exitCode = process.waitFor();
            System.out.println("命令执行结束，退出码：" + exitCode);

            stopWatch.stop();
            System.out.println("执行脚本耗时：" + stopWatch.getTotalTimeMillis());
        } catch (Exception e) {
            // 打印异常堆栈跟踪
            e.printStackTrace();
            // 抛出业务异常
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "执行生成器脚本错误");
        }

        stopWatch = new StopWatch();
        stopWatch.start();
        // 9.生成代码的位置
        // 构造生成代码的目录路径
        String generatedPath = scriptDir.getAbsolutePath() + "/generated";
        // 构造结果压缩包文件路径
        String resultPath = tempDirPath + "/result.zip";
        // 使用ZipUtil工具类将生成的代码目录压缩成zip文件
        File resultFile = ZipUtil.zip(generatedPath, resultPath);
        stopWatch.stop();
        System.out.println("压缩结果耗时：" + stopWatch.getTotalTimeMillis());

        // 10.下载文件
        // 10.1 设置响应头
        // 设置响应的内容类型为二进制流
        response.setContentType("application/octet-stream;charset=UTF-8");
        // 设置响应头，指定文件名，以便浏览器下载
        response.setHeader("Content-Disposition", "attachment; filename=" + resultFile.getName());
        // 10.2 写入响应
        // 将生成的压缩包文件写入到HTTP响应的输出流中
        Files.copy(resultFile.toPath(), response.getOutputStream());

        // 11.清理文件
        // 使用CompletableFuture异步执行文件清理操作
        CompletableFuture.runAsync(() -> {
            // 删除临时目录及其内容
            FileUtil.del(tempDirPath);
        });
    }


    /**
     * 制作代码生成器
     *
     * @param generatorMakeRequest
     * @param request
     * @param response
     */
    @PostMapping("/make")
    public void makeGenerator(@RequestBody GeneratorMakeRequest generatorMakeRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1）输入参数
        String zipFilePath = generatorMakeRequest.getZipFilePath();
        Meta meta = generatorMakeRequest.getMeta();

        // 需要登录
        User loginUser = userService.getLoginUser(request);

        // 2）创建独立工作空间，下载压缩包到本地
        if (StrUtil.isBlank(zipFilePath)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "压缩包不存在");
        }

        // 工作空间
        String projectPath = System.getProperty("user.dir");
        // 随机 id
        String id = IdUtil.getSnowflakeNextId() + RandomUtil.randomString(6);
        String tempDirPath = String.format("%s/.temp/make/%s", projectPath, id);
        String localZipFilePath = tempDirPath + "/project.zip";

        // 新建文件
        if (!FileUtil.exist(localZipFilePath)) {
            FileUtil.touch(localZipFilePath);
        }

        try {
            cosManager.download(zipFilePath, localZipFilePath);
        } catch (InterruptedException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "压缩包下载失败");
        }

        // 3）解压，得到项目模板文件
        File unzipDistDir = ZipUtil.unzip(localZipFilePath);

        // 4）构造 meta 对象和输出路径
        String sourceRootPath = unzipDistDir.getAbsolutePath();
        meta.getFileConfig().setSourceRootPath(sourceRootPath);
        MetaValidator.doValidAndFill(meta);
        String outputPath = String.format("%s/generated/%s", tempDirPath, meta.getName());

        // 5）调用 maker 方法制作生成器
        GenerateTemplate generateTemplate = new ZipGenerator();
        try {
            generateTemplate.doGenerate(outputPath, meta);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "制作失败");
        }

        // 6）下载压缩的产物包文件
        String suffix = "-dist.zip";
        String zipFileName = meta.getName() + suffix;
        String distZipFilePath = outputPath + suffix;

        // 下载文件
        // 设置响应头
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName);
        // 写入响应
        Files.copy(Paths.get(distZipFilePath), response.getOutputStream());

        // 7）清理文件
        CompletableFuture.runAsync(() -> {
            FileUtil.del(tempDirPath);
        });
    }

    /**
     * 缓存代码生成器
     * @param generatorCacheRequest
     * @param request
     * @param response
     */
    @PostMapping("/cache")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public void cacheGenerator(@RequestBody GeneratorCacheRequest generatorCacheRequest, HttpServletRequest request, HttpServletResponse response) {
        if (null == generatorCacheRequest || generatorCacheRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 获取生成器
        Long requestId = generatorCacheRequest.getId();
        Generator generator = generatorService.getById(requestId);
        if (null == generator) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }

        String distPath = generator.getDistPath();
        if (StrUtil.isBlank(distPath)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "产物包不存在");
        }

        // 缓存空间
        String zipFilePath = getCacheFilePath(requestId, distPath);

        // 新建文件
        if (!FileUtil.exist(zipFilePath)) {
            FileUtil.touch(zipFilePath);
        }

        // 下载生成器
        try {
            cosManager.download(distPath, zipFilePath);
        } catch (InterruptedException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "压缩包下载失败");
        }
    }

    /**
     * 获取缓存文件路径
     *
     * @param id 生成器的 ID，用于标识不同的生成器实例
     * @param distPath 生成器的存储路径，指定了生成器文件在存储系统中的位置
     * @return 返回生成器缓存文件的完整路径
     */
    public String getCacheFilePath(long id, String distPath) {
        // 获取当前项目的路径
        String projectPath = System.getProperty("user.dir");
        // 构造临时目录路径，用于存放缓存文件
        String tempDirPath = String.format("%s/.temp/cache/%s", projectPath, id);
        // 构造缓存文件的完整路径
        String zipFilePath = String.format("%s/%s", tempDirPath, distPath);
        // 返回缓存文件的完整路径
        return zipFilePath;
    }


}
